﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using IM.Easy.Core.Options;

namespace IM.Easy.Core.Security
{
    public static class JwtEncryption
    {
        private static JwtOptions _options = new();

        public static void Configure(JwtOptions options)
        {
            _options = options;
        }

        /// <summary>
        /// 生成加密的 JWT 令牌
        /// </summary>
        /// <param name="secretKey">用于加密的密钥</param>
        /// <param name="claims">令牌中的声明信息</param>
        /// <param name="expiration">令牌的过期时间</param>
        /// <returns>生成的 JWT 令牌字符串</returns>
        public static string EncryptToken(Dictionary<string, object> claims)
        {
            var secretKey = _options.IssuerSigningKey;
            // 验证输入参数
            if (string.IsNullOrEmpty(secretKey))
                throw new ArgumentException("密钥不能为空", nameof(secretKey));
            if (claims == null || !claims.Any())
                throw new ArgumentException("声明信息不能为空", nameof(claims));

            // 创建密钥和签名凭据
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            // 创建 JWT 令牌
            var token = new JwtSecurityToken(
                issuer: _options.ValidIssuer,
                audience: _options.ValidAudience,
                claims: claims.Select(c => new Claim(c.Key, c.Value?.ToString() ?? string.Empty)),
                expires: DateTime.Now.AddMinutes(_options.ExpiredTime),
                signingCredentials: creds
            );

            // 返回生成的令牌字符串
            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        /// <summary>
        /// 解密 JWT 令牌并验证其有效性
        /// </summary>
        /// <param name="token">需要解密的 JWT 令牌字符串</param>
        /// <param name="secretKey">用于解密的密钥</param>
        /// <returns>验证通过的声明主体</returns>
        public static ClaimsPrincipal DecryptToken(string token)
        {
            var secretKey = _options.IssuerSigningKey;
            // 验证输入参数
            if (string.IsNullOrEmpty(token))
                throw new ArgumentException("令牌不能为空", nameof(token));
            if (string.IsNullOrEmpty(secretKey))
                throw new ArgumentException("密钥不能为空", nameof(secretKey));

            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(secretKey);

            // 设置验证参数
            var validationParams = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = _options.ValidateIssuerSigningKey, // 验证签名密钥
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = _options.ValidateIssuer,          // 不验证 Issuer
                ValidateAudience = _options.ValidateAudience,        // 不验证 Audience
                ValidateLifetime = _options.ValidateLifetime,         // 验证生命周期
                ClockSkew = TimeSpan.Zero        // 取消默认的 5 分钟的时间偏差
            };

            try
            {
                // 验证令牌并返回声明主体
                return tokenHandler.ValidateToken(token, validationParams, out _);
            }
            catch (SecurityTokenExpiredException)
            {
                throw new SecurityTokenExpiredException("令牌已过期");
            }
            catch (SecurityTokenException)
            {
                throw new SecurityTokenException("令牌无效");
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("令牌解密失败", ex);
            }
        }

        /// <summary>
        /// 从令牌中提取声明信息
        /// </summary>
        /// <param name="token">JWT 令牌字符串</param>
        /// <returns>提取出的声明信息字典</returns>
        public static Dictionary<string, string> ExtractClaims(string token)
        {
            if (string.IsNullOrEmpty(token))
                throw new ArgumentException("令牌不能为空", nameof(token));

            var handler = new JwtSecurityTokenHandler();
            var jwtToken = handler.ReadJwtToken(token);

            // 提取所有的声明并转换为字典
            return jwtToken.Claims.ToDictionary(c => c.Type, c => c.Value);
        }

        /// <summary>
        /// 验证令牌是否有效
        /// </summary>
        /// <param name="token">需要验证的 JWT 令牌字符串</param>
        /// <param name="secretKey">验证使用的密钥</param>
        /// <returns>如果令牌有效则返回 true，否则返回 false</returns>
        public static bool ValidateToken(string token)
        {
            try
            {
                // 解密和验证令牌，如果无异常则表示令牌有效
                DecryptToken(token);
                return true;
            }
            catch
            {
                return false;
            }
        }
    }

}
